#include "serial.h"
#include <stdio.h>
#include <iostream>

Serial::Serial(const std::string &device)
    : fd(-1), devicePath(device), isOpen(false)
{
}

Serial::~Serial()
{
    if (isOpen)
    {
        close();
    }
}

bool Serial::open()
{
    // 打开串口设备
    fd = ::open(devicePath.c_str(), O_RDWR | O_NOCTTY);
    if (fd < 0)
    {
        std::cerr << "Failed to open " << devicePath << std::endl;
        return false;
    }

    // 配置串口参数 (默认9600 8N1)
    if (!configure(B9600, 8, 'N', 1))
    {
        ::close(fd);
        fd = -1;
        return false;
    }

    isOpen = true;
    return true;
}

void Serial::close()
{
    if (fd >= 0)
    {
        tcdrain(fd); // 等待所有数据传输完成
        ::close(fd);
        fd = -1;
    }
    isOpen = false;
}

bool Serial::configure(int baudrate, int dataBits, char parity, int stopBits)
{
    struct termios opt;
    memset(&opt, 0, sizeof(opt));
    tcgetattr(fd, &opt);

    // 输入模式标志
    opt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
    opt.c_iflag &= ~INPCK; // 无奇偶校验

    // 输出模式标志
    opt.c_oflag &= ~OPOST;

    // 控制模式标志
    opt.c_cflag |= (CLOCAL | CREAD);
    opt.c_cflag &= ~CSIZE;

    // 设置数据位
    switch (dataBits)
    {
    case 5:
        opt.c_cflag |= CS5;
        break;
    case 6:
        opt.c_cflag |= CS6;
        break;
    case 7:
        opt.c_cflag |= CS7;
        break;
    default:
        opt.c_cflag |= CS8;
        break;
    }

    // 设置校验位
    switch (parity)
    {
    case 'o':
    case 'O':
        opt.c_cflag |= PARENB;
        opt.c_cflag |= PARODD;
        break;
    case 'e':
    case 'E':
        opt.c_cflag |= PARENB;
        opt.c_cflag &= ~PARODD;
        break;
    default: // 'n', 'N' 或其他
        opt.c_cflag &= ~PARENB;
        break;
    }

    // 设置停止位
    if (stopBits == 2)
        opt.c_cflag |= CSTOPB;
    else
        opt.c_cflag &= ~CSTOPB;

    // 无硬件流控
    opt.c_cflag &= ~CRTSCTS;

    // 本地模式标志
    opt.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);

    // 特殊字符
    opt.c_cc[VMIN] = 1;  // 至少读到1字节才返回
    opt.c_cc[VTIME] = 0; // 阻塞等待

    // 设置波特率
    cfsetispeed(&opt, baudrate);
    cfsetospeed(&opt, baudrate);

    // 应用设置
    int status = tcsetattr(fd, TCSANOW, &opt);
    tcflush(fd, TCIOFLUSH);

    return status == 0;
}

bool Serial::write(const SerialData &data)
{
    if (!isOpen || data.data.empty())
    {
        return false;
    }

    ssize_t bytesWritten = ::write(fd, data.data.data(), data.data.size());
    return bytesWritten == static_cast<ssize_t>(data.data.size());
}

SerialData Serial::read(int timeout_ms)
{
    if (!isOpen)
    {
        return SerialData();
    }

    fd_set readfds;
    struct timeval tv;
    unsigned char buf[256];
    SerialData result;

    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);

    // 设置超时时间
    tv.tv_sec = timeout_ms / 1000;
    tv.tv_usec = (timeout_ms % 1000) * 1000;

    int ret = select(fd + 1, &readfds, NULL, NULL, &tv);
    if (ret > 0)
    {
        ssize_t bytesRead = ::read(fd, buf, sizeof(buf));
        if (bytesRead > 0)
        {
            result.data.assign(buf, buf + bytesRead);
        }
    }

    return result;
}

SerialData Serial::buildFrame(int boardId, unsigned char commandId, int lockId)
{
    // 创建不包含校验字节的帧
    std::vector<unsigned char> frame = {
        0x57, 0x4B, 0x4C, 0x59, 0x09,
        static_cast<unsigned char>(boardId),
        commandId,
        static_cast<unsigned char>(lockId)};

    // 计算校验字节 (从第一个字节到最后一个数据字节的XOR)
    unsigned char checksum = 0;
    for (const unsigned char &byte : frame)
    {
        checksum ^= byte; // 异或运算
    }

    // 添加校验字节到帧末尾
    frame.push_back(checksum);

    return SerialData(frame);
}

bool Serial::openLock(int boardId, int lockId)
{
    if (!isOpen)
    {
        if (!open())
        {
            return false;
        }
    }

    // 构造开锁数据帧
    SerialData lockFrame = buildFrame(boardId, 0x82, lockId);

    // 发送开锁指令
    if (!write(lockFrame))
    {
        std::cerr << "Failed to send open lock command" << std::endl;
        return false;
    }

    std::cout << "Send: " << lockFrame.toString() << std::endl;

    // 读取响应
    SerialData response = read(1000); // 1秒超时

    if (response.data.empty())
    {
        std::cerr << "No response from device" << std::endl;
        return false;
    }

    std::cout << "Received: " << response.toString() << std::endl;

    // 解析响应
    // 0x57 0x4B 0x4C 0x59 0x0B 0x00 0x82 状态（1）+通道号（1）+锁状态（1）XOR
    if (response.data.size() >= 11 && // 回复帧长度至少为11字节
        response.data[0] == 0x57 && response.data[1] == 0x4B &&
        response.data[2] == 0x4C && response.data[3] == 0x59 &&
        response.data[5] == boardId && response.data[6] == 0x82)
    {

        // 检查状态字段 (位置7)
        if (response.data[7] == 0x00)
        {
            // 执行成功，检查锁状态 (位置9)
            return (response.data[9] == 0x00); // 0x00表示门已打开
        }
        else if (response.data[7] == 0xFF)
        {
            std::cerr << "Command execution failed" << std::endl;
            return false;
        }
    }

    std::cerr << "Invalid response format" << std::endl;
    return false;
}

bool Serial::queryLock(int boardId, int lockId)
{
    if (!isOpen)
    {
        if (!open())
        {
            return false;
        }
    }

    // 构造查询锁状态数据帧
    SerialData queryFrame = buildFrame(boardId, 0x83, lockId);

    // 发送查询指令
    if (!write(queryFrame))
    {
        std::cerr << "Failed to send query lock command" << std::endl;
        return false;
    }

    std::cout << "Send: " << queryFrame.toString() << std::endl;

    // 读取响应
    SerialData response = read(1000); // 1秒超时

    if (response.data.empty())
    {
        std::cerr << "No response from device" << std::endl;
        return false;
    }

    std::cout << "Received: " << response.toString() << std::endl;

    // 解析响应
    // 0x57 0x4B 0x4C 0x59 0x0B 0x00 0x83 状态(1)+通道号(1)+锁状态(1) XOR
    if (response.data.size() >= 11 && // 回复帧长度至少为11字节
        response.data[0] == 0x57 && response.data[1] == 0x4B &&
        response.data[2] == 0x4C && response.data[3] == 0x59 &&
        response.data[5] == boardId && response.data[6] == 0x83)
    {
        // 检查状态字段 (位置7)
        if (response.data[7] == 0x00)
        {
            // 执行成功，检查锁状态 (位置9)
            if (response.data[9] == 0x00)
            {
                std::cout << "Door is open" << std::endl;
                return true; // 门打开
            }
            else if (response.data[9] == 0x01)
            {
                std::cout << "Door is closed" << std::endl;
                return false; // 门关闭
            }
            else
            {
                std::cerr << "Unknown lock status: " << static_cast<int>(response.data[9]) << std::endl;
                return false;
            }
        }
        else if (response.data[7] == 0xFF)
        {
            std::cerr << "Command execution failed" << std::endl;
            return false;
        }
    }

    std::cerr << "Invalid response format" << std::endl;
    return false;
}

bool Serial::openAllLocks(int boardId)
{
    if (!isOpen)
    {
        if (!open())
        {
            return false;
        }
    }

    // 构造开全部锁的数据帧
    std::vector<unsigned char> frame = {
        0x57, 0x4B, 0x4C, 0x59, 0x08,
        static_cast<unsigned char>(boardId),
        0x86 // 指令字
        // 无数据域
    };

    // 计算校验字节 (从第一个字节到最后一个数据字节的XOR)
    unsigned char checksum = 0;
    for (const unsigned char &byte : frame)
    {
        checksum ^= byte;
    }
    frame.push_back(checksum);

    SerialData allLockFrame(frame);

    // 发送开全部锁指令
    if (!write(allLockFrame))
    {
        std::cerr << "Failed to send open all locks command" << std::endl;
        return false;
    }

    std::cout << "Send: " << allLockFrame.toString() << std::endl;

    // 读取响应
    SerialData response = read(1000); // 1秒超时

    if (response.data.empty())
    {
        std::cerr << "No response from device" << std::endl;
        return false;
    }

    std::cout << "Received: " << response.toString() << std::endl;

    // 解析响应
    // 0x57 0x4B 0x4C 0x59 0x09 板号 0x86 状态(1) XOR
    if (response.data.size() >= 9 &&
        response.data[0] == 0x57 && response.data[1] == 0x4B &&
        response.data[2] == 0x4C && response.data[3] == 0x59 &&
        response.data[5] == static_cast<unsigned char>(boardId) &&
        response.data[6] == 0x86)
    {
        // 检查状态字段 (位置7)
        if (response.data[7] == 0x00)
        {
            // 执行成功
            return true;
        }
        else if (response.data[7] == 0xFF)
        {
            std::cerr << "Command execution failed" << std::endl;
            return false;
        }
    }

    std::cerr << "Invalid response format" << std::endl;
    return false;
}